﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DecoderCache.cs
* 文件说明: 解码器缓存类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 解码器缓存类
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class DecoderCache<TKey, TValue> : IDisposable where TValue : BaseFrameDecoder
    {
        private readonly Timer m_timer;
        IList<TKey> removeList = new List<TKey>();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="aliveMinutes">存活时间</param>
        public DecoderCache(int aliveMinutes = 5)
        {
            AliveMinutes = Math.Max(1, aliveMinutes);

            m_timer = new Timer((o) =>
            {
                DateTime now = DateTime.Now;
                removeList.Clear();

                foreach (DecoderCacheItem<TKey, TValue> obj in Cache.Values)
                {
                    TimeSpan span = now - obj.LastAccessTime;
                    if (span.TotalMinutes > AliveMinutes)
                    {
                        removeList.Add(obj.Key);
                    }
                }

                foreach (TKey key in removeList)
                {
                    Remove(key);
                }
            },
            null,
            0,
            60 * 1000);
        }

        /// <summary>
        /// 获取解码器
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TValue this[TKey key] => Get(key);

        /// <summary>
        /// 对象存活时间（分钟）。如果一个对象在 AliveMinutes 时间间隔内都不被访问，则将被从缓存中清除。
        /// </summary>
        public int AliveMinutes { get; set; } = 5;

        private readonly ConcurrentDictionary<TKey, DecoderCacheItem<TKey, TValue>> Cache = new();

        /// <summary>
        /// 解码器是否存在
        /// </summary>
        /// <param name="key">Key</param>
        /// <returns>是否存在</returns>
        public bool ContainsKey(TKey key)
        {
            return key != null && Cache.ContainsKey(key);
        }

        /// <summary>
        /// 增加解码器
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">解码器</param>
        public void Add(TKey key, TValue value)
        {
            if (key == null) return;
            Remove(key);
            var item = new DecoderCacheItem<TKey, TValue>(key, value);
            Cache.TryAdd(key, item);
            OnAddDecoder?.Invoke(this, item);
        }

        /// <summary>
        /// 移除解码器
        /// </summary>
        /// <param name="key"></param>
        public void Remove(TKey key)
        {
            if (key == null) return;

            if (Cache.ContainsKey(key))
            {
                OnRemoveDecoder?.Invoke(this, Cache[key]);
                Cache[key].Dispose();
                Cache.TryRemove(key, out _);
            }
        }

        /// <summary>
        /// 移除解码器事件
        /// </summary>
        public event OnDecoderChanged<TKey, TValue> OnRemoveDecoder;

        /// <summary>
        /// 移除解码器事件
        /// </summary>
        public event OnDecoderChanged<TKey, TValue> OnAddDecoder;

        /// <summary>
        /// 清空
        /// </summary>
        public void Clear()
        {
            if (Cache.Count > 0)
            {
                foreach (var item in Cache.Values)
                {
                    OnRemoveDecoder?.Invoke(this, item);
                    item.Dispose();
                }

                Cache.Clear();
            }
        }

        /// <summary>
        /// 获取解码器
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TValue Get(TKey key)
        {
            if (key == null)
            {
                return null;
            }

            if (Cache.ContainsKey(key))
            {
                return Cache[key].Value;
            }

            return null;
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            Clear();
        }

        /// <summary>
        /// 客户端列表
        /// </summary>
        public ICollection<TKey> Keys => Cache.Keys;

        /// <summary>
        /// 缓存节点列表
        /// </summary>
        public ICollection<DecoderCacheItem<TKey, TValue>> Values => Cache.Values;
    }

    /// <summary>
    /// 移除解码器事件
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="sender"></param>
    /// <param name="item"></param>
    public delegate void OnDecoderChanged<TKey, TValue>(object sender, DecoderCacheItem<TKey, TValue> item) where TValue : BaseFrameDecoder;

    /// <summary>
    /// 解码器缓存节点
    /// </summary>
    /// <typeparam name="TKey">Key</typeparam>
    /// <typeparam name="TValue">解码器</typeparam>
    public class DecoderCacheItem<TKey, TValue> : IDisposable where TValue : BaseFrameDecoder
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public DecoderCacheItem(TKey key, TValue value)
        {
            Key = key;
            _value = value;
            LastAccessTime = DateTime.Now;
        }

        private TValue _value;

        /// <summary>
        /// Key
        /// </summary>
        public TKey Key { get; private set; }

        /// <summary>
        /// 解码器
        /// </summary>
        public TValue Value
        {
            get
            {
                LastAccessTime = DateTime.Now;
                return _value;
            }
        }

        /// <summary>
        /// 最后更新时间
        /// </summary>
        public DateTime LastAccessTime { get; private set; }

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            _value.Dispose();
        }
    }
}
